package cn.texous.web.parsing.engine.config;

import cn.texous.util.commons.constant.Result;
import cn.texous.util.commons.constant.ResultCode;
import cn.texous.util.commons.exception.BusinessException;
import cn.texous.util.commons.util.GsonUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * controller 异常统一处理配置
 *
 * @author Showa.L
 * @since 2019/4/28 10:43
 */
@Slf4j
@ControllerAdvice
public class AnaExceptionHandlerConfig {

    private ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 捕获可预见异常
     *
     * @param response 结果返回
     * @param ex       异常
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public ModelAndView handlerBusinessException(HttpServletResponse response,
                                                 BusinessException ex) {
        log.error(String.format("businesss exception【code=%s, desc=%s】",
                ex.getCode(), ex.getMessage()), ex);
        Result result = Result.out(ex.getCode(), ex.getMessage(), null);
        return responseResult(response, result);
    }

    /**
     * 捕获可预见异常
     *
     * @param response 结果返回
     * @param ex       异常
     * @return
     */
    @ExceptionHandler(BindException.class)
    public ModelAndView handlerBindException(HttpServletResponse response,
                                             BindException ex) {
        String message = ex.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
        log.error(String.format("bind exception【code=%s, desc=%s】",
                ResultCode.PARAM_FAIL.getCode(), message));
        Result result = Result.out(ResultCode.PARAM_FAIL.getCode(), message, null);
        return responseResult(response, result);
    }

    /**
     * 参数校验异常拦截 @RequestBody
     *
     * @param response 结果返回
     * @param ex       异常信息
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ModelAndView validationErrorHandler(HttpServletResponse response,
                                               MethodArgumentNotValidException ex) {
        // 同样是获取BindingResult对象，然后获取其中的错误信息
        // 如果前面开启了fail_fast，事实上这里只会有一个信息
        // 如果没有，则可能又多个
        List<String> errorInformation = ex.getBindingResult().getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.toList());
        String error = GsonUtils.toJson(errorInformation);
        log.error("MethodArgumentNotValidException: {}", error);
        Result result = Result.out(ResultCode.PARAM_FAIL.getCode(), error, null);
        return responseResult(response, result);
    }

    /**
     * 参数校验异常拦截 @PathVariable以及@RequestParam
     *
     * @param response 结果返回
     * @param ex       异常信息
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ModelAndView validationErrorHandler(HttpServletResponse response,
                                               ConstraintViolationException ex) {
        List<String> errorInformation = ex.getConstraintViolations()
                .stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());
        String error = GsonUtils.toJson(errorInformation);
        log.error("ConstraintViolationException: {}", error);
        Result result = Result.out(ResultCode.PARAM_FAIL.getCode(), error, null);
        return responseResult(response, result);
    }

    /**
     * 捕获不可预见异常
     *
     * @param request  请求
     * @param response 结果
     * @param e        异常
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ModelAndView handlerException(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Exception e) {
        Result result;
        String errorMsg;
        if (e instanceof NoHandlerFoundException) {
            errorMsg = "api [" + request.getRequestURI() + "] not exists";
            result = Result.out(ResultCode.NOT_FOUND);
        } else {
            errorMsg = "api [" + request.getRequestURI() + "] error，plase later try again!";
            result = Result.out(ResultCode.INTERNAL_SERVER_ERROR);
        }
        log.error(String.format("handler exception: 【%s】", errorMsg), e);
        return responseResult(response, result);
    }

    private ModelAndView responseResult(HttpServletResponse response, Result result) {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        response.setStatus(200);
        try {
            response.getWriter().write(objectMapper.writeValueAsString(result));
        } catch (IOException ex) {
            log.error(ex.getMessage());
        }
        return new ModelAndView();
    }


}
